#include <stdio.h>
#include <cstddef>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>

#include "mpp_common.h"


#include <rockchip/rk_type.h>
#include <stdio.h>
#include <rockchip/mpp_err.h>
#include <rockchip/mpp_buffer.h>
#include <rockchip/vpu_api.h>
#include <rockchip/mpp_task.h>
#include <rockchip/mpp_packet.h>
#include <rockchip/mpp_meta.h>
#include <rockchip/mpp_frame.h>
#include <rockchip/mpp_rc_api.h>
#include <rockchip/mpp_rc_defs.h>
#include <rockchip/rk_mpi.h>
#include <string.h>
#include <iostream>
#include <pthread.h>
using namespace std;
#include "mpp_itc_dec_interface.h"
#define PKT_SIZE  (16*1024)




class  mpp_decode
{
    public:
        mpp_decode();
        ~mpp_decode();
        RET_CODE mpp_decode_init(const CODE_TYPE type , const FRAME_TYPE decFormat,
                                                        const uint32_t height,const uint32_t width,
                                                        const uint32_t desHeight,const uint32_t desWidth,
                                                        const bool isDisplay);
        MPP_DECODE_BUF *mpp_dec_create_buffer();
        void  mpp_dec_destroy_buffer(MPP_DECODE_BUF **buf);
        void  mpp_dec_copy_buffer(MPP_DECODE_BUF *decode_buf,void *h26x_buf,int32_t length); 
        RET_CODE mpp_push( MPP_DECODE_BUF *frame,bool isEnd);
        void mpp_set_buffer_length(MPP_DECODE_BUF *frame,uint32_t length);
        int dump_mpp_frame(MppFrame frame);
        RET_CODE mpp_dump_frame(const int fd, uint8_t **date,size_t *length);
        void mpp_decode_regist(void *ctx,void *usr_arg,decode_frame_callback callback);
        RET_CODE mpp_deinit();
    private:
        uint64_t get_time();
        RET_CODE  decode_one_pkt(uint8_t *buf, uint32_t size, MppFrame *srcFrm, MppFrame *dstFrm);
        CODE_TYPE type;
        uint32_t mFps;
        uint32_t mEos;
        uint32_t mID;
        uint32_t mDisPlay;
        uint32_t mFrmCnt;
        uint32_t srcW;
        uint32_t srcH;
        uint32_t dstW;
        uint32_t dstH;

        RK_S64 mTimeS;
        RK_S64 mTimeE;
        RK_S64 mTimeDiff;
        double mFrmRate;

        FILE *mFin;
        FILE *mFout;
        uint8_t *mPktBuf;

        uint8_t *mOutBuffer;

        MppCtx mCtx;
        MppApi *mApi;
        MppPacket mPkt;
        MppBuffer mBuffer;
        MppBufferGroup mFrmGrp;


        MppFrameFormat mDestFormat;

        void *ctx;
         void *usr_arg ;
        MppFrame srcFrm;
        // class decode_frame
        // {
        //     public:
        //     MppFrame srcFrm;
        //     //MppFrame dstFrm;
        //     uint32_t length ;
        //     decode_frame():srcFrm(NULL),length(0)//,dstFrm(NULL)
        //     {}
        //     decode_frame & operator =(const decode_frame & str) 
        //     {
        //         if (this != &str){
        //             this->srcFrm=str.srcFrm;
        //             //this->dstFrm=str.dstFrm;
        //         }
        //         return *this;
        //     }
        // };

        
        // MPP_DECODE_BUF mBuf;

        bool isDisplay;

        vector<decode_frame_callback> mCallback;
};
RET_CODE mpp_decode::mpp_deinit()
{
    int ret  = 0;
    if (mPkt) {
        mpp_packet_deinit(&mPkt);
        mPkt = NULL;
    }
    

    if (mPktBuf) {
        delete(mPktBuf);
        mPktBuf = NULL;
    }
    
    if (mBuffer) {
        mpp_buffer_put(mBuffer);
        mBuffer = NULL;
    }

    if (mFrmGrp) {
        mpp_buffer_group_put(mFrmGrp);
        mFrmGrp = NULL;
    }

    if(mCtx){
        ret = mApi->reset(mCtx);
        if (ret != MPP_OK) {
            cxx_log("failed to exec mApi->reset.\n");
        }
    }
    if (mCtx) {
        mpp_destroy(mCtx);
        mCtx = NULL;
    }
    return MPP_RET_CODE_OK;
}
mpp_decode::mpp_decode():mFps(0),
    mEos(0),
    mID(0),
    mDisPlay(0),
    mFrmCnt(0),
    mFrmRate(0.0),
    mTimeS(0),
    mTimeE(0),
    mTimeDiff(0),
    mFin(NULL),
    mFout(NULL),
    mPktBuf(NULL),
    mCtx(NULL),
    mApi(NULL),
    mPkt(NULL),
    mFrmGrp(NULL),
    mBuffer(NULL),
    mDestFormat(MPP_FMT_YUV420SP),
    srcFrm(NULL),
    ctx(NULL),
    usr_arg(NULL),
    mOutBuffer(NULL)
{
    mCallback.clear();
}
mpp_decode::~mpp_decode()
{
    int ret  = 0;
    if (mPkt) {
        mpp_packet_deinit(&mPkt);
        mPkt = NULL;
    }

    if (mPktBuf) {
        delete(mPktBuf);
        mPktBuf = NULL;
    }

    if (mBuffer) {
        mpp_buffer_put(mBuffer);
        mBuffer = NULL;
    }

    if (mFrmGrp) {
        mpp_buffer_group_put(mFrmGrp);
        mFrmGrp = NULL;
    }

    if(mCtx){
        ret = mApi->reset(mCtx);
        if (ret != MPP_OK) {
            cxx_log("failed to exec mApi->reset.\n");
        }
    }
    if (mCtx) {
        mpp_destroy(mCtx);
        mCtx = NULL;
    }


    if(this->srcFrm){
            cout<<"销毁编码器缓存"<<endl;
            mpp_frame_deinit(&this->srcFrm);
    }    
     if(this->mOutBuffer){
         delete this->mOutBuffer;
         this->mOutBuffer = NULL;
     }

    mCallback.clear();

}
uint64_t mpp_decode::get_time()
{
    struct timeval tv_date;
    gettimeofday(&tv_date, NULL);
    return (uint64_t)tv_date.tv_sec * 1000000 + (RK_S64)tv_date.tv_usec;
}
RET_CODE mpp_decode::mpp_decode_init(const CODE_TYPE type, const FRAME_TYPE decFormat,
                                        const uint32_t height, const uint32_t width,
                                        const uint32_t desHeight,const uint32_t desWidth,
                                        const bool isDisplay)
{
    int ret = 0;
    int x, y, i;
    RK_U32 need_split = 1;
    MpiCmd mpi_cmd = MPP_CMD_BASE;
    MppParam param = NULL;
    MppCodingType mType;

    //mDisPlay =(int) display;
    srcW = width;
    srcH = height;
    dstW = desWidth;
    dstH = desHeight;

    mPktBuf = new uint8_t[PKT_SIZE];     //申请一个空间
    if (!mPktBuf) {
        cxx_log("failed to malloc mPktBuf.\n");
        return  MPP_RET_CODE_FAILURE;
    }

    ret = mpp_packet_init(&mPkt, mPktBuf, PKT_SIZE);
    if (ret) {
        cxx_log("failed to exec mpp_packet_init ret %d", ret);
        return MPP_RET_CODE_FAILURE;
    }

    ret = mpp_create(&mCtx, &mApi);
    if (ret != MPP_OK) {
        cxx_log("failed to exec mpp_create ret %d", ret);
        return MPP_RET_CODE_FAILURE;
    }

    mpi_cmd = MPP_DEC_SET_PARSER_SPLIT_MODE;
    param = &need_split;
    ret = mApi->control(mCtx, mpi_cmd, param);
    if (ret != MPP_OK) {
        cxx_log("failed to control MPP_DEC_SET_PARSER_SPLIT_MODE.\n");
        return  MPP_RET_CODE_FAILURE;
    }

    switch (type) {
        case  MPP_TYPE_H264:
            mType = MPP_VIDEO_CodingAVC;
            break;
        case  MPP_TYPE_H265:
            mType = MPP_VIDEO_CodingHEVC;
            break;
        case MPP_TYPE_MJPEG:
            mType = MPP_VIDEO_CodingMJPEG;
            break;
        
        case MPP_TYPE_MPEG4:
            mType = MPP_VIDEO_CodingMPEG4;
            break;

        case MPP_TYPE_FAILURE:
            return MPP_RET_CODE_FAILURE;

        default:  return  MPP_RET_CODE_FAILURE;

    }

    ret = mpp_init(mCtx, MPP_CTX_DEC, mType);
    if (ret != MPP_OK) {
        cxx_log("failed to exec mpp_init.\n");
        return MPP_RET_CODE_FAILURE;
    }
    return MPP_RET_CODE_OK;
}
MPP_DECODE_BUF *mpp_decode::mpp_dec_create_buffer()
{
    //decode_frame *frame  = NULL;
    MPP_DECODE_BUF *Buf = NULL;
    int  ret = 0;
    int hor_stride = CODEC_ALIGN(dstW, 16);
    int ver_srride = CODEC_ALIGN(dstH, 16);
    Buf = new MPP_DECODE_BUF;
    if(mPktBuf && Buf){
        Buf->addr = mPktBuf;
        Buf->length = PKT_SIZE;
        Buf->extend = (void *)NULL;
        cout<<"创建编码器缓存成功"<<endl;
        return Buf;
    }
    err:
        
        if(Buf){
            delete Buf;
        }

    return NULL;
}
void  mpp_decode::mpp_dec_destroy_buffer(MPP_DECODE_BUF **buf)
{
    if((*buf)){
        delete (*buf);
        (*buf) = NULL;
        cout<<"销毁编码器缓存"<<endl;
    }
}
void  mpp_decode::mpp_dec_copy_buffer(MPP_DECODE_BUF *decode_buf,void *h26x_buf,int32_t length)
{
    // decode_frame *frame  = NULL;
    if(!decode_buf || !h26x_buf  ||  !length)
        return ;
    // frame = (decode_frame *)decode_buf->extend;
    // if(!frame)
        // return ;
    if(length <=  PKT_SIZE  ) {
        decode_buf->length = length;
        memcpy(decode_buf->addr,h26x_buf,length);    

    }else{
        cout<<"拷贝内存错误"<<endl;
    }
}
void mpp_decode::mpp_set_buffer_length(MPP_DECODE_BUF *frame,uint32_t length)
{
    
    // decode_frame *_frame  = NULL;
    if(!length && !frame){
        cout<<"设置数据长度失败"<<length<<endl;
        return ;
    }
        
    // _frame = (decode_frame *)frame->extend;
    // if(!_frame){
    //     cout<<"设置数据长度失败"<<length<<endl;
    //     return ;
    // }
        
    if(length <=   PKT_SIZE  ) {
        //cout<<"设置数据长度"<<length<<endl;
        frame->length = length; 
    }
}



RET_CODE mpp_decode::mpp_push( MPP_DECODE_BUF *frameBuf,bool isEnd)
{
   
    double timeDiff;
    int ret = 0;




    // mTimeS = get_time();
    if(frameBuf->length >PKT_SIZE||  frameBuf->length <= 0){
        cxx_log("输入长度不正确. %d \n" ,frameBuf->length);
        return MPP_RET_CODE_FAILURE;
    }
        

    size_t read_size = frameBuf->length;
    if (isEnd) {
        cxx_log("found last packet.\n");
        mEos = 1;
    }






    ret = decode_one_pkt(mPktBuf, read_size, &this->srcFrm, NULL);
    if (ret < 0) {
        cxx_log("failed to exec decode.\n");
        return MPP_RET_CODE_FAILURE;
    }

    // mTimeE = get_time();
    // timeDiff = double(mTimeE - mTimeS)/1000;
    // mFrmRate = (mFrmCnt * 1000) / timeDiff;
    // cxx_log("decode frames %d using %.2fms frm rate:%.2f.\n", mFrmCnt,
    //         timeDiff, mFrmRate);


    return MPP_RET_CODE_OK;
}



#if 0

RET_CODE mpp_decode::mpp_push(const MPP_DECODE_BUF *frame,bool isEnd)
{
    MppFrame srcFrm;
    MppFrame dstFrm;
    double timeDiff;
    int ret = 0;
    int hor_stride = CODEC_ALIGN(dstW, 16);
    int ver_srride = CODEC_ALIGN(dstH, 16);

    ret = mpp_frame_init(&dstFrm);
    if (ret) {
        cxx_log("failed to exec mpp_frame_init %d.\n", ret);
        return MPP_RET_CODE_FAILURE;
    }

    mpp_buffer_get(mFrmGrp, &mBuffer, hor_stride * ver_srride * 3 / 2); //--如果想解码成其他公式这边就可能需要修改参数
    mpp_frame_set_width(dstFrm, dstW);
    mpp_frame_set_height(dstFrm, dstH);
    mpp_frame_set_hor_stride(dstFrm, hor_stride);
    mpp_frame_set_ver_stride(dstFrm, ver_srride);
    mpp_frame_set_fmt(dstFrm, MPP_FMT_YUV420SP);    //解码出来默认NV12
    mpp_frame_set_buffer(dstFrm, mBuffer);


    mTimeS = get_time();
    if(length>PKT_SIZE||length <= 0){
        cxx_log("输入长度不正确.\n");
        return MPP_RET_CODE_FAILURE;
    }
        
    while (!mEos) {
        size_t read_size = length;
        if (isEnd) {
            cxx_log("found last packet.\n");
            mEos = 1;
        }

        mpp_frame_set_eos(dstFrm, mEos);

        ret = decode_one_pkt(mPktBuf, read_size, &srcFrm, &dstFrm);
        if (ret < 0) {
            cxx_log("failed to exec decode.\n");
            return MPP_RET_CODE_FAILURE;
        }
    }
    mpp_frame_deinit(&dstFrm);

    mTimeE = get_time();
    timeDiff = double(mTimeE - mTimeS)/1000;
    mFrmRate = (mFrmCnt * 1000) / timeDiff;
    cxx_log("decode frames %d using %.2fms frm rate:%.2f.\n", mFrmCnt,
            timeDiff, mFrmRate);


    return MPP_RET_CODE_OK;
}

#endif


void mpp_decode::mpp_decode_regist(void *ctx, void *usr_arg, decode_frame_callback callback)
{
    this->ctx = ctx;
    this->usr_arg = usr_arg;
    mCallback.push_back(callback);
}

int mpp_decode::dump_mpp_frame(MppFrame frame)
{
    uint32_t width = 0;
    uint32_t height = 0;
    uint32_t h_stride = 0;
    uint32_t v_stride = 0;
    MppFrameFormat fmt = MPP_FMT_YUV420SP;
    MppBuffer buffer = NULL;
    uint8_t *base = NULL;

    if (!frame)
        return -1;

    width = mpp_frame_get_width(frame);
    height = mpp_frame_get_height(frame);
    h_stride = mpp_frame_get_hor_stride(frame);
    v_stride = mpp_frame_get_ver_stride(frame);
    fmt = mpp_frame_get_fmt(frame);
    buffer = mpp_frame_get_buffer(frame);

    if(!buffer)
        return -2;

    base = (uint8_t *)mpp_buffer_get_ptr(buffer);
    //printf("========fmt %d       frame size %d ========\n",fmt,mpp_frame_get_buf_size(frame));
    if(base){
        uint32_t i =0;
        uint8_t *base_y = base;
        uint8_t *base_c = base + h_stride * v_stride;
        
        if(!this->mOutBuffer){
            this->mOutBuffer = new uint8_t[height*width*3/2];
            memset(this->mOutBuffer,0x00,height*width*3/2);
        }
        
        if(this->mOutBuffer){
            
            for (i = 0; i < height; i++, base_y += h_stride) {
                memcpy(&this->mOutBuffer[i*width],base_y,width);
           
            }
            for (i = 0; i < height / 2; i++, base_c += h_stride) {
                //fwrite(base_c, 1, width, fp);
                memcpy(&this->mOutBuffer[(height*width)+(i*width )],base_c,width);
            }
            
            for(int i = 0;i<mCallback.size();i++){
                        mCallback.at(i)(this->ctx ,this->usr_arg,mpp_buffer_get_fd(buffer),//mpp_frame_get_buffer(frame)
                                (uint8_t *)this->mOutBuffer,
                                 height*width+(height / 2)*(width / 2)+(height / 2)*(width / 2));//mpp_buffer_get_size(buffer)
            }
        }
        
    }

    return 0;
}
RET_CODE mpp_decode::decode_one_pkt(uint8_t *buf, uint32_t size, MppFrame *srcFrm, MppFrame *dstFrm)
{
    int ret = 0;
    RK_U32 pkt_done = 0;

    mpp_packet_write(mPkt, 0, buf, size);
    mpp_packet_set_pos(mPkt, buf);
    mpp_packet_set_length(mPkt, size);

    if (mEos)
        mpp_packet_set_eos(mPkt);

    do {
        RK_S32 times = 5;

        if (!pkt_done) {
            ret = mApi->decode_put_packet(mCtx, mPkt);
            if (ret == MPP_OK)
                pkt_done = 1;
        }


        do{
            RK_S32 get_frm = 0;
            RK_U32 frm_eos = 0;

try_again:
            ret = mApi->decode_get_frame(mCtx, srcFrm);
            if (ret == MPP_ERR_TIMEOUT) {
                if (times > 0) {
                    times--;
                    usleep(1000);
                    goto try_again;
                }
                cxx_log("decode_get_frame failed too much time.\n");
            }
            if (ret != MPP_OK) {
                cxx_log("decode_get_frame failed ret %d.\n", ret);
                break;
            }

            if (*srcFrm) {
                //如果帧有改变
                if (mpp_frame_get_info_change(*srcFrm)) {
                    RK_U32 width = mpp_frame_get_width(*srcFrm);
                    RK_U32 height = mpp_frame_get_height(*srcFrm);
                    RK_U32 hor_stride = mpp_frame_get_hor_stride(*srcFrm);
                    RK_U32 ver_stride = mpp_frame_get_ver_stride(*srcFrm);

                    cxx_log("decode_get_frame get info changed found.\n");
                    cxx_log("decoder require buffer w:h [%d:%d] stride [%d:%d]",
                            width, height, hor_stride, ver_stride);

                    ret = mpp_buffer_group_get_internal(&mFrmGrp, MPP_BUFFER_TYPE_DRM);
                    if (ret) {
                        cxx_log("get mpp buffer group failed ret %d.\n", ret);
                        break;
                    }

                    mApi->control(mCtx, MPP_DEC_SET_EXT_BUF_GROUP, mFrmGrp);
                    mApi->control(mCtx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
                } else {
                    cxx_log("decode_get_frame ID:%d get srcFrm %d.\n", mID, mFrmCnt++);
                    dump_mpp_frame(*srcFrm);
                }
                frm_eos = mpp_frame_get_eos(*srcFrm);
                mpp_frame_deinit(srcFrm);
                *srcFrm = NULL;
                get_frm = 1;
            }

            if (mEos && pkt_done && !frm_eos) {
                usleep(10000);
                continue;
            }

            if (frm_eos) {
                cxx_log("found last frame.\n");
                break;
            }

            if (get_frm)
                continue;
            break;


        }while(1);


        if (pkt_done)
            break;
        /*
         * why sleep here?
         * mpp has a internal input pakcet list,if it is full, wait here 3ms to
         * wait internal list isn't full.
         */
        usleep(100);
    }while(1);



    return MPP_RET_CODE_OK;
}
RET_CODE mpp_decode::mpp_dump_frame(const int fd, uint8_t **date,size_t *length)
{
    return MPP_RET_CODE_OK;
}

typedef struct
{
    mpp_decode *decoder;
    MPP_ITC_DEC_FUNTION *decode;

}decoder_contex;

static RET_CODE  mpp_decoder_init(void *ctx,const CODE_TYPE type , const FRAME_TYPE decFormat,
        const uint32_t height,const uint32_t width,
        const uint32_t dstHeight,const uint32_t dstWidth, 
        const bool isDisplay)
{
    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_decode_init(type,decFormat,height,width,dstHeight,dstWidth,isDisplay);
    return MPP_RET_CODE_FAILURE;
}
static MPP_DECODE_BUF * mpp_dec_create_buffer(void *ctx)
{
    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_dec_create_buffer( );
    return NULL;
}
static  void mpp_dec_detroy_buffer(void *ctx,MPP_DECODE_BUF **buf)
{
    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_dec_destroy_buffer( buf);
    
}
static void  mpp_dec_copy_buffer(void *ctx,MPP_DECODE_BUF *decode_buf,void *h26x_buf,int32_t length)
{
    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
         contex->decoder->mpp_dec_copy_buffer(decode_buf,h26x_buf,length);
   
}
static  void  mpp_dec_set_buffer_length(void *ctx,MPP_DECODE_BUF *decode_buf,int32_t length)
 {
    
     decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
         contex->decoder->mpp_set_buffer_length(decode_buf,length);
 }

static RET_CODE  mpp_decoder_push(void *ctx,MPP_DECODE_BUF *buf,bool isEnd)
{

    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_push(buf,isEnd);
    return MPP_RET_CODE_FAILURE;
}
static RET_CODE  mpp_decoder_dump_Frame(void *ctx,const int fd,uint8_t **output,size_t *length)
{

    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_dump_frame(fd, output,length);
    return MPP_RET_CODE_FAILURE;
}
static RET_CODE  mpp_decoder_deinit(void *ctx)
{

    decoder_contex *contex =  (decoder_contex*)ctx;
    if(contex)
        return contex->decoder->mpp_deinit();
    return MPP_RET_CODE_FAILURE;
}

static void mpp_decoder_regist_done_callback(void *ctx, void *usr_arg, decode_frame_callback callback)
{
    decoder_contex *contex = (decoder_contex*)ctx;
    if(contex)
        contex->decoder->mpp_decode_regist(ctx,usr_arg,callback);
}


void dec_test()
{
    printf("NexyHo dec_test\n");
}


MPP_ITC_DEC_FUNTION * create_decode()
{
    mpp_decode *decoder = new mpp_decode();
    decoder_contex *ctx = new decoder_contex();
    MPP_ITC_DEC_FUNTION *decode = new MPP_ITC_DEC_FUNTION;
    if(decode){
        decode->mpp_dec_deinit = mpp_decoder_deinit;
        decode->mpp_dec_dump_Frame=mpp_decoder_dump_Frame;
        
        decode->mpp_dec_init=mpp_decoder_init;

        decode->mpp_dec_create_buffer   =   mpp_dec_create_buffer;
        decode->mpp_dec_detroy_buffer  =   mpp_dec_detroy_buffer;
        decode->mpp_dec_copy_buffer     =   mpp_dec_copy_buffer;
        decode-> mpp_dec_set_buffer_length  = mpp_dec_set_buffer_length;
        decode->mpp_dec_push       =   mpp_decoder_push;
        decode->mpp_dec_regist_done_callback = mpp_decoder_regist_done_callback;

        ctx->decoder = decoder;
        ctx->decode = decode;
        decode->ctx = ctx;
        //decode->mpp_dec_int = NULL;
        //decode->mpp_dec_input = NULL;
        //decode->mpp_dec_getFrame = NULL;
        //decode->mpp_dec_deinit = NULL; 
    }
    return decode;
}
void destroy(MPP_ITC_DEC_FUNTION * dec)
{
    if(dec){
        decoder_contex *ctx=(decoder_contex*)dec->ctx;
        if(ctx->decoder){
            delete ctx->decoder;
            ctx->decoder = NULL;
        }
        delete ctx;
        ctx = NULL;
        delete dec;
        dec = NULL;
    }
}
